﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Caca.MineSweeper.Tests
{
    public class TestHelper
    {
        #region GenerateField

        public static MineField GenerateField(int[] fields, int width, int height)
        {
            //int width = stringField[0].Length;
            //int height = stringField.Length;

            //int[] fields = GenerateIntField(stringField);

            List<Plot> plots = new List<Plot>(width * height);
            foreach (var f in fields)
            {
                if (f >= 0)
                {
                    plots.Add(new NonMinePlot(f));
                }
                else
                {
                    plots.Add(new MinePlot());
                }
            }

            return new MineField(width, height, plots);
        }

        public static int[] GenerateIntField(string[] stringField)
        {
            int width = stringField[0].Length;
            int height = stringField.Length;

            int[] fields = new int[width * height];
            for (var h = 0; h < height; h++)
                for (var w = 0; w < width; w++)
                {
                    var c = stringField[h].ToUpper()[w];

                    switch (c)
                    {
                        case 'X':
                            //X 代表地雷,  将周围的非地雷格计数+1
                            AddSurroundCount(width, height, fields, w, h);
                            fields[w + width * h] = -1;     // -1 代表地雷格
                            break;

                        case 'O':
                            break;
                    }
                }
            return fields;
        }

        /// <summary>
        /// 在一个矩阵中, 将围绕指定座标, 周围的8个方格中的值+1, 注意, 不会增加超出矩阵边界的方格值.
        /// 座标原点为矩阵左上角顶点, 为0,0
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="fields">一维矩阵数据</param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public static void AddSurroundCount(int width, int height, int[] fields, int x, int y)
        {
            int[,] surround = new int[8, 2]{
                                {x-1,y-1},
                                {x, y-1},
                                {x+1, y-1},
                                {x-1, y},
                                {x+1, y},
                                {x-1, y+1},
                                {x, y+1},
                                {x+1, y+1}
                            };

            for (int i = 0; i < 8; i++)
            {
                var tempx = surround[i, 0];
                var tempy = surround[i, 1];
                if (tempx >= 0 && tempy >= 0 && tempx < width && tempy < height)    //边界判断
                    if (fields[tempx + width * tempy] >= 0)                     //是否是地雷格判断, 地雷格是-1
                        fields[tempx + width * tempy]++;
            }
        }

        public static void ValidField(MineField field)
        {
            int width = field.Width;
            int height = field.Height;
            IList<Plot> plots = field.Plots;

            //验证每一个非地雷格的周围地雷数是否正确
            for (int i = 0; i < plots.Count; i++)
            {
                if (plots[i] is MinePlot)
                    continue;

                int x, y;
                IndexToPosition(i, out x, out y, width);
                int mines = CalculateSurroundMines(field, x, y);

                Assert.AreEqual(mines, (plots[i] as NonMinePlot).SurroundMines, string.Format("Index: {0}", i));
            }
        }

        public static int CalculateSurroundMines(MineField field, int x, int y)
        {
            int width = field.Width;
            int height = field.Height;
            IList<Plot> plots = field.Plots;

            int[,] surround = { 
                {-1,-1},                  
                {0,-1},                  
                {1,-1},                  
                {-1,0},                  
                {1,0},                  
                {-1,1},                  
                {0,1},                  
                {1,1}
            };

            int mines = 0;
            for (int j = 0; j < surround.GetLength(0); j++)
            {
                var tempx = x + surround[j, 0];
                var tempy = y + surround[j, 1];
                if (ValidPosition(tempx, tempy, width, height))
                {
                    int index = PositionToIndex(tempx, tempy, width);
                    if (plots[index] is MinePlot)
                        mines++;
                }
            }
            return mines;
        }

        #endregion

        #region Helper

        private static int PositionToIndex(int x, int y, int width)
        {
            return x + y * width;
        }

        private static bool ValidPosition(int x, int y, int width, int height)
        {
            return x >= 0 && y >= 0 && x < width && y < height;
        }

        public static void IndexToPosition(int i, out int x, out int y, int width)
        {
            x = i % width;
            y = i / width;
        }

        public static string ArrayToString(int[] fields, int width)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < fields.Length; i++)
            {
                sb.Append(fields[i]);
                if ((i + 1) % width == 0)
                    sb.AppendLine();
            }
            return sb.ToString();
        }

        public static void ArrayCompare(int[] expected, int[] fields)
        {
            for (int i = 0; i < expected.Length; i++)
                Assert.AreEqual(expected[i], fields[i], string.Format("Index: {0}", i));
        }

        #endregion

        internal static string FieldToIntMatrix(MineField field)
        {
            return FieldToString(field, m => "*", m => m.SurroundMines);
        }

        internal static string FieldToString(MineField field)
        {
            return FieldToString(field, m => "X", m => "O");
        }

        private static string FieldToString(MineField field, Func<MinePlot, object> mineValue, Func<NonMinePlot, object> nonMineValue)
        {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < field.Plots.Count; i++)
            {
                var p = field.Plots[i];

                if (p is NonMinePlot)
                {
                    sb.AppendFormat("{0}", nonMineValue(p as NonMinePlot));
                }
                else if (p is MinePlot)
                {
                    sb.AppendFormat("{0}", mineValue(p as MinePlot));
                }

                if ((i + 1) % field.Width == 0)
                {
                    sb.AppendLine();
                }
            }

            return sb.ToString();
        }
    }
}
